fix directory browser + implement changelog viewer
[vuplus_dvbapp] / lib / python / Plugins / SystemPlugins / NFIFlash / downloader.py
1 # -*- coding: utf-8 -*-
2 from Components.MenuList import MenuList
3 from Screens.Screen import Screen
4 from Screens.MessageBox import MessageBox
5 from Screens.ChoiceBox import ChoiceBox
6 from Components.ActionMap import ActionMap
7 from Components.Sources.StaticText import StaticText
8 from Components.Sources.Progress import Progress
9 from Components.Label import Label
10 from Components.FileList import FileList
11 from Components.MultiContent import MultiContentEntryText
12 from Components.ScrollLabel import ScrollLabel
13 from Tools.Directories import fileExists
14 from Tools.HardwareInfo import HardwareInfo
15 from enigma import eConsoleAppContainer, eListbox, gFont, eListboxPythonMultiContent, \
16         RT_HALIGN_LEFT, RT_HALIGN_CENTER, RT_VALIGN_CENTER, RT_WRAP, eRect, eTimer
17 from os import system, remove
18 import re
19 import urllib
20 from Tools.Downloader import downloadWithProgress
21 from twisted.web import client
22 from twisted.internet import reactor, defer
23 from twisted.python import failure
24 from Plugins.SystemPlugins.Hotplug.plugin import hotplugNotifier
25
26 class UserRequestedCancel(Exception):
27         pass
28
29 class Feedlist(MenuList):
30         def __init__(self, list=[], enableWrapAround = False):
31                 MenuList.__init__(self, list, enableWrapAround, eListboxPythonMultiContent)
32                 self.l.setFont(0, gFont("Regular", 16))
33                 self.l.setItemHeight(22)
34
35         def clear(self):
36                 del self.list[:]
37                 self.l.setList(self.list)
38
39         def getNFIname(self):
40                 l = self.l.getCurrentSelection()
41                 return l and l[0][0]
42
43         def getNFIurl(self):
44                 l = self.l.getCurrentSelection()
45                 return l and l[0][1]
46
47         def getNFOname(self):
48                 l = self.l.getCurrentSelection()
49                 return l and l[0][0][:-3]+"nfo"
50
51         def getNFOurl(self):
52                 l = self.l.getCurrentSelection()
53                 return l and l[0][1][:-3]+"nfo"
54
55         def isValid(self):
56                 l = self.l.getCurrentSelection()
57                 if l[0] == 0:
58                         return False
59                 else:
60                         return True
61
62         def moveSelection(self,idx=0):
63                 if self.instance is not None:
64                         self.instance.moveSelectionTo(idx)
65
66 class NFOViewer(Screen):
67         skin = """
68                 <screen name="NFOViewer" position="110,115" size="540,400" title="Changelog viewer" >
69                         <widget name="changelog" position="10,10" size="520,380" font="Regular;16" />
70                 </screen>"""
71
72         def __init__(self, session, nfo):
73                 Screen.__init__(self, session)
74                 self["changelog"] = ScrollLabel(nfo)
75
76                 self["ViewerActions"] = ActionMap(["SetupActions", "ColorActions", "DirectionActions"],
77                         {
78                                 "green": self.exit,
79                                 "red": self.exit,
80                                 "ok": self.exit,
81                                 "cancel": self.exit,
82                                 "down": self.pageDown,
83                                 "up": self.pageUp                       
84                         })
85         def pageUp(self):
86                 self["changelog"].pageUp()
87
88         def pageDown(self):
89                 self["changelog"].pageDown()
90                         
91         def exit(self):
92                 self.close(False)
93
94 class NFIDownload(Screen):
95         LIST_SOURCE = 1
96         LIST_DEST = 2
97         skin = """
98                 <screen name="NFIDownload" position="90,95" size="560,420" title="Image download utility">
99                         <ePixmap pixmap="skin_default/buttons/red.png" position="0,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
100                         <ePixmap pixmap="skin_default/buttons/green.png" position="140,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
101                         <ePixmap pixmap="skin_default/buttons/yellow.png" position="280,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
102                         <ePixmap pixmap="skin_default/buttons/blue.png" position="420,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
103                         <widget source="key_red" render="Label" position="0,0" zPosition="1" size="140,40" font="Regular;19" valign="center" halign="center" backgroundColor="#9f1313" transparent="1" />
104                         <widget source="key_green" render="Label" position="140,0" zPosition="1" size="140,40" font="Regular;19" valign="center" halign="center" backgroundColor="#1f771f" transparent="1" />
105                         <widget source="key_yellow" render="Label" position="280,0" zPosition="1" size="140,40" font="Regular;19" valign="center" halign="center" backgroundColor="#a08500" transparent="1" />
106                         <widget source="key_blue" render="Label" position="420,0" zPosition="1" size="140,40" font="Regular;19" valign="center" halign="center" backgroundColor="#18188b" transparent="1" />
107                         
108                         <widget source="label_top" render="Label" position="10,44" size="240,20" font="Regular;16" />
109                         <widget name="feedlist" position="10,66" size="250,222" scrollbarMode="showOnDemand" />
110                         <widget name="destlist" position="0,66" size="260,222" scrollbarMode="showOnDemand" />
111
112                         <widget source="label_bottom" render="Label" position="10,312" size="240,18" font="Regular;16"/>
113                         <widget source="path_bottom" render="Label" position="10,330" size="250,42" font="Regular;18" />
114                         
115                         <widget source="infolabel" render="Label" position="270,44" size="280,284" font="Regular;16" />
116                         <widget source="job_progressbar" render="Progress" position="10,374" size="540,26" borderWidth="1" backgroundColor="#254f7497" />
117                         <widget source="job_progresslabel" render="Label" position="130,378" zPosition="2" font="Regular;18" halign="center" transparent="1" size="300,22" foregroundColor="#000000" />
118                         <widget source="statusbar" render="Label" position="10,404" size="540,16" font="Regular;16" foregroundColor="#cccccc" />
119                 </screen>"""
120
121         def __init__(self, session, destdir="/tmp/"):
122                 self.skin = NFIDownload.skin
123                 Screen.__init__(self, session)
124                 
125                 self["job_progressbar"] = Progress()
126                 self["job_progresslabel"] = StaticText()
127                 
128                 self["infolabel"] = StaticText()
129                 self["statusbar"] = StaticText()
130                 self["label_top"] = StaticText()
131                 self["label_bottom"] = StaticText()
132                 self["path_bottom"] = StaticText()
133                 
134                 self["key_green"] = StaticText()
135                 self["key_yellow"] = StaticText()
136                 self["key_blue"] = StaticText()
137
138                 self["key_red"] = StaticText()
139
140                 self["feedlist"] = Feedlist([0,(eListboxPythonMultiContent.TYPE_TEXT, 0, 0,250, 30, 0, RT_HALIGN_LEFT|RT_VALIGN_CENTER, "feed not available")])
141                 self["destlist"] = FileList(destdir, showDirectories = True, showFiles = False)
142                 self["destlist"].hide()
143
144                 self.download_container = eConsoleAppContainer()
145                 self.nfo = ""
146                 self.nfofile = ""
147                 self.feedhtml = ""
148                 self.focus = None
149                 self.download = None
150                 self.box = HardwareInfo().get_device_name()
151                 self.feed_base = "http://www.dreamboxupdate.com/opendreambox/1.5/%s/images/" % self.box
152                 self.nfi_filter = "" # "release" # only show NFIs containing this string, or all if ""
153                 self.wizard_mode = False
154
155                 self["actions"] = ActionMap(["OkCancelActions", "ColorActions", "DirectionActions", "EPGSelectActions"],
156                 {
157                         "cancel": self.closeCB,
158                         "red": self.closeCB,
159                         "green": self.nfi_download,
160                         "yellow": self.switchList,
161                         "blue": self.askCreateUSBstick,
162                         "prevBouquet": self.switchList,
163                         "nextBouquet": self.switchList,
164                         "ok": self.ok,
165                         "left": self.left,
166                         "right": self.right,
167                         "up": self.up,
168                         "upRepeated": self.up,
169                         "downRepeated": self.down,
170                         "down": self.down
171                 }, -1)
172
173                 self.feed_download()
174
175         def downloading(self, state=True):
176                 if state is True:       
177                         self["key_red"].text = _("Cancel")
178                         self["key_green"].text = ""
179                         self["key_yellow"].text = ""
180                         self["key_blue"].text = ""
181                 else:
182                         self.download = None
183                         self["key_red"].text = _("Exit")
184                         if self["feedlist"].isValid():
185                                 self["key_green"].text = (_("Download"))
186                                 if self.focus is self.LIST_SOURCE:
187                                         self["key_yellow"].text = (_("Change dir."))
188                                 else:
189                                         self["key_yellow"].text = (_("Select image"))
190                         self["key_blue"].text = (_("USB stick wizard"))
191
192         def switchList(self,to_where=None):
193                 if self.download or not self["feedlist"].isValid():
194                         return
195
196                 self["job_progressbar"].value = 0
197                 self["job_progresslabel"].text = ""
198
199                 if to_where is None:
200                         if self.focus is self.LIST_SOURCE:
201                                 to_where = self.LIST_DEST
202                         if self.focus is self.LIST_DEST:
203                                 to_where = self.LIST_SOURCE
204
205                 if to_where is self.LIST_DEST:
206                         self.focus = self.LIST_DEST
207                         self["statusbar"].text = _("Please select target directory or medium")
208                         self["label_top"].text = _("choose destination directory")+":"
209                         self["feedlist"].hide()
210                         self["destlist"].show()
211                         self["label_bottom"].text = _("Selected source image")+":"
212                         self["path_bottom"].text = str(self["feedlist"].getNFIname())
213                         self["key_yellow"].text = (_("Select image"))
214
215                 elif to_where is self.LIST_SOURCE:
216                         self.focus = self.LIST_SOURCE
217                         self["statusbar"].text = _("Please choose .NFI image file from feed server to download")
218                         self["label_top"].text = _("select image from server")+":"
219                         self["feedlist"].show()
220                         self["destlist"].hide()
221                         self["label_bottom"].text = _("Destination directory")+":"
222                         self["path_bottom"].text = str(self["destlist"].getCurrentDirectory())
223                         self["key_yellow"].text = (_("Change dir."))
224
225         def up(self):
226                 if self.download:
227                         return
228                 if self.focus is self.LIST_SOURCE:
229                         self["feedlist"].up()
230                         self.nfo_download()
231                 if self.focus is self.LIST_DEST:
232                         self["destlist"].up()
233
234         def down(self):
235                 if self.download:
236                         return
237                 if self.focus is self.LIST_SOURCE:
238                         self["feedlist"].down()
239                         self.nfo_download()
240                 if self.focus is self.LIST_DEST:
241                         self["destlist"].down()
242
243         def left(self):
244                 if self.download:
245                         return
246                 if self.focus is self.LIST_SOURCE:
247                         self["feedlist"].pageUp()
248                         self.nfo_download()
249                 if self.focus is self.LIST_DEST:
250                         self["destlist"].pageUp()
251
252         def right(self):
253                 if self.download:
254                         return
255                 if self.focus is self.LIST_SOURCE:
256                         self["feedlist"].pageDown()
257                         self.nfo_download()
258                 if self.focus is self.LIST_DEST:
259                         self["destlist"].pageDown()
260
261         def ok(self):
262                 if self.focus is self.LIST_SOURCE and self.nfo:
263                         self.session.open(NFOViewer, self.nfo)
264                 if self.download:
265                         return
266                 if self.focus is self.LIST_DEST:
267                         if self["destlist"].canDescent():
268                                 self["destlist"].descent()
269
270         def feed_download(self):
271                 self.downloading(True)
272                 self.download = self.feed_download
273                 client.getPage(self.feed_base).addCallback(self.feed_finished).addErrback(self.feed_failed)
274
275         def feed_failed(self, failure_instance):
276                 print "[feed_failed] " + str(failure_instance)
277                 self["infolabel"].text = _("Could not connect to Dreambox .NFI Image Feed Server:") + "\n" + failure_instance.getErrorMessage() + "\n\n" + _("Please check your network settings!")
278                 self.downloading(False)
279
280         def feed_finished(self, feedhtml):
281                 print "[feed_finished] " + str(feedhtml)
282                 self.downloading(False)
283                 fileresultmask = re.compile("<a class=[\'\"]nfi[\'\"] href=[\'\"](?P<url>.*?)[\'\"]>(?P<name>.*?.nfi)</a>", re.DOTALL)
284                 searchresults = fileresultmask.finditer(feedhtml)
285                 fileresultlist = []
286                 if searchresults:
287                         for x in searchresults:
288                                 url = x.group("url")
289                                 if url[0:7] != "http://":
290                                         url = self.feed_base + x.group("url")
291                                 name = x.group("name")
292                                 if name.find(self.nfi_filter) > -1:
293                                         entry = [[name, url],(eListboxPythonMultiContent.TYPE_TEXT, 0, 0,250, 30, 0, RT_HALIGN_LEFT|RT_VALIGN_CENTER, name)]
294                                         print "adding to feedlist: " + str(entry)
295                                         fileresultlist.append(entry)
296                                 else:
297                                         print "NOT adding to feedlist: " + name
298                         self["feedlist"].l.setList(fileresultlist)
299                         self["feedlist"].moveSelection(0)
300
301                 if len(fileresultlist) > 0:
302                         self.switchList(self.LIST_SOURCE)
303                         self.nfo_download()
304                 else:
305                         self["infolabel"].text = _("Cannot parse feed directory")
306
307         def nfo_download(self):
308                 print "[check_for_NFO]"
309                 if self["feedlist"].isValid():
310                         print "nfiname: " + self["feedlist"].getNFIname()
311                         self["job_progressbar"].value = 0
312                         self["job_progresslabel"].text = ""
313                         if self["feedlist"].getNFIurl() is None:
314                                 self["key_green"].text = ""
315                                 return
316                         self["key_green"].text = _("Download")
317                         nfourl = self["feedlist"].getNFOurl()
318                         print "downloading " + nfourl
319                         self.download = self.nfo_download
320                         self.downloading(True)
321                         client.getPage(nfourl).addCallback(self.nfo_finished).addErrback(self.nfo_failed)
322                         self["statusbar"].text = ("Downloading image description...")
323
324         def nfo_failed(self, failure_instance):
325                 print "[nfo_failed] " + str(failure_instance)
326                 self["infolabel"].text = _("No details for this image file") + "\n" + self["feedlist"].getNFIname()
327                 self["statusbar"].text = ""
328                 self.nfofilename = ""
329                 self.nfo = ""
330                 self.downloading(False)
331
332         def nfo_finished(self,nfodata=""):
333                 print "[nfo_finished] " + str(nfodata)
334                 self.downloading(False)
335                 self.nfo = nfodata
336                 if self.nfo != "":
337                         self.nfofilename = self["destlist"].getCurrentDirectory() + '/' + self["feedlist"].getNFOname()
338                         self["infolabel"].text = self.nfo
339                 else:   
340                         self.nfofilename = ""
341                         self["infolabel"].text = _("No details for this image file")
342                 self["statusbar"].text = _("Press OK to view full changelog")
343
344         def nfi_download(self):
345                 if self["destlist"].getCurrentDirectory() is None:
346                         self.switchList(self.LIST_TARGET)
347                 if self["feedlist"].isValid():
348                         url = self["feedlist"].getNFIurl()
349                         self.nfilocal = self["destlist"].getCurrentDirectory()+'/'+self["feedlist"].getNFIname()
350                         print "[nfi_download] downloading %s to %s" % (url, self.nfilocal)
351                         self.download = downloadWithProgress(url,self.nfilocal)
352                         self.download.addProgress(self.nfi_progress)
353                         self["job_progressbar"].range = 1000
354                         self.download.start().addCallback(self.nfi_finished).addErrback(self.nfi_failed)
355                         self.downloading(True)
356
357         def nfi_progress(self, recvbytes, totalbytes):
358                 #print "[update_progress] recvbytes=%d, totalbytes=%d" % (recvbytes, totalbytes)
359                 self["job_progressbar"].value = int(1000*recvbytes/float(totalbytes))
360                 self["job_progresslabel"].text = "%d of %d kBytes (%.2f%%)" % (recvbytes/1024, totalbytes/1024, 100*recvbytes/float(totalbytes))
361
362         def nfi_failed(self, failure_instance=None, error_message=""):
363                 if error_message == "" and failure_instance is not None:
364                         error_message = failure_instance.getErrorMessage()
365                 print "[nfi_failed] " + error_message
366                 if fileExists(self["destlist"].getCurrentDirectory()+'/'+self["feedlist"].getNFIname()):
367                         message = "%s %s\n%s" % (_(".NFI Download failed:"), error_message, _("Remove the incomplete .NFI file?"))
368                         self.session.openWithCallback(self.nfi_remove, MessageBox, message, MessageBox.TYPE_YESNO)
369                 else:
370                         message = "%s %s" % (_(".NFI Download failed:"),error_message)
371                         self.session.open(MessageBox, message, MessageBox.TYPE_ERROR)
372                         self.downloading(False)
373
374         def nfi_finished(self, string=""):
375                 print "[nfi_finished] " + str(string)
376                 if self.nfo != "":
377                         self.nfofilename = self["destlist"].getCurrentDirectory() + '/' + self["feedlist"].getNFOname()
378                         nfofd = open(self.nfofilename, "w")
379                         if nfofd:
380                                 nfofd.write(self.nfo)
381                                 nfofd.close()
382                         else:
383                                 print "couldn't save nfo file " + self.nfofilename
384
385                         pos = self.nfo.find("MD5:")
386                         if pos > 0 and len(self.nfo) >= pos+5+32:
387                                 self["statusbar"].text = ("Please wait for md5 signature verification...")
388                                 cmd = "md5sum -c -"
389                                 md5 = self.nfo[pos+5:pos+5+32] + "  " + self.nfilocal
390                                 print cmd, md5
391                                 self.download_container.setCWD(self["destlist"].getCurrentDirectory())
392                                 self.download_container.appClosed.append(self.md5finished)
393                                 self.download_container.execute(cmd)
394                                 self.download_container.write(md5)
395                                 self.download_container.dataSent.append(self.md5ready)
396                         else:
397                                 self["statusbar"].text = "Download completed."
398                                 self.downloading(False)
399                 else:
400                         self["statusbar"].text = "Download completed."
401                         self.downloading(False)
402                         if self.wizard_mode:
403                                 self.configBackup()
404
405         def md5ready(self, retval):
406                 self.download_container.sendEOF()
407
408         def md5finished(self, retval):
409                 print "[md5finished]: " + str(retval)
410                 self.download_container.appClosed.remove(self.md5finished)
411                 if retval==0:
412                         self.downloading(False)
413                         if self.wizard_mode:
414                                 self.configBackup()
415                         else:
416                                 self["statusbar"].text = _(".NFI file passed md5sum signature check. You can safely flash this image!")
417                                 self.switchList(self.LIST_SOURCE)
418                 else:
419                         self.session.openWithCallback(self.nfi_remove, MessageBox, (_("The md5sum validation failed, the file may be downloaded incompletely or be corrupted!") + "\n" + _("Remove the broken .NFI file?")), MessageBox.TYPE_YESNO)
420
421         def nfi_remove(self, answer):
422                 self.downloading(False)
423                 if answer == True:
424                         nfifilename =  self["destlist"].getCurrentDirectory()+'/'+self["feedlist"].getNFIname()
425                         if fileExists(self.nfofilename):
426                                 remove(self.nfofilename)
427                         if fileExists(nfifilename):
428                                 remove(nfifilename)
429                 self.switchList(self.LIST_SOURCE)
430
431         def askCreateUSBstick(self):
432                 self.downloading()
433                 self.imagefilename = "/tmp/nfiflash_" + self.box + ".img"
434                 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.")
435                 self.session.openWithCallback(self.flasherdownload_query, MessageBox, (message + '\n' + _("First we need to download the latest boot environment for the USB flasher.")), MessageBox.TYPE_YESNO)
436
437         def flasherdownload_query(self, answer):
438                 if answer is False:
439                         self.downloading(False)
440                         self.switchList(self.LIST_SOURCE)
441                         return
442                 #url = self.feed_base + "/nfiflasher_" + self.box + ".tar.bz2"
443                 url = "http://www.dreamboxupdate.com/download/opendreambox/dreambox-nfiflasher-%s.tar.bz2" % self.box
444                 localfile = "/tmp/nfiflasher_image.tar.bz2"
445                 print "[flasherdownload_query] downloading %s to %s" % (url, localfile)
446                 self["statusbar"].text = ("Downloading %s..." % url)
447                 self.download = downloadWithProgress(url,localfile)
448                 self.download.addProgress(self.nfi_progress)
449                 self["job_progressbar"].range = 1000
450                 self.download.start().addCallback(self.flasherdownload_finished).addErrback(self.flasherdownload_failed)
451
452         def flasherdownload_failed(self, failure_instance=None, error_message=""):
453                 if error_message == "" and failure_instance is not None:
454                         error_message = failure_instance.getErrorMessage()
455                 print "[flasherdownload_failed] " + error_message
456                 message = "%s %s" % (_("Download of USB flasher boot image failed: "),error_message)
457                 self.session.open(MessageBox, message, MessageBox.TYPE_ERROR)
458                 self.remove_img(True)
459
460         def flasherdownload_finished(self, string=""):
461                 print "[flasherdownload_finished] " + str(string)       
462                 self.container = eConsoleAppContainer()
463                 self.container.appClosed.append(self.umount_finished)
464                 self.container.dataAvail.append(self.tool_avail)
465                 self.taskstring = ""
466                 umountdevs = ""
467                 from os import listdir
468                 for device in listdir("/dev"):
469                         if device[:2] == "sd" and device[-1:].isdigit():
470                                 umountdevs += "/dev/"+device
471                 self.cmd = "umount " + umountdevs
472                 print "executing " + self.cmd
473                 self.container.execute(self.cmd)
474
475         def tool_avail(self, string):
476                 print "[tool_avail]" + string
477                 self.taskstring += string
478
479         def umount_finished(self, retval):
480                 self.container.appClosed.remove(self.umount_finished)
481                 self.container.appClosed.append(self.dmesg_cleared)
482                 self.taskstring = ""
483                 self.cmd = "dmesg -c"
484                 print "executing " + self.cmd
485                 self.container.execute(self.cmd)
486
487         def dmesg_cleared(self, answer):
488                 self.container.appClosed.remove(self.dmesg_cleared)
489                 self.msgbox = self.session.open(MessageBox, _("Please disconnect all USB devices from your Dreambox and (re-)attach the target USB stick (minimum size is 64 MB) now!"), MessageBox.TYPE_INFO)
490                 hotplugNotifier.append(self.hotplugCB)
491
492         def hotplugCB(self, dev, action):
493                 print "[hotplugCB]", dev, action
494                 if dev.startswith("sd") and action == "add":
495                         self.msgbox.close()
496                         hotplugNotifier.remove(self.hotplugCB)
497                         self.container.appClosed.append(self.dmesg_scanned)
498                         self.taskstring = ""
499                         self.cmd = "dmesg"
500                         print "executing " + self.cmd
501                         self.container.execute(self.cmd)
502
503         def dmesg_scanned(self, retval):
504                 self.container.appClosed.remove(self.dmesg_scanned)
505                 dmesg_lines = self.taskstring.splitlines()
506                 self.devicetext = None
507                 self.stickdevice = None
508                 for i, line in enumerate(dmesg_lines):
509                         if line.find("usb-storage: waiting for device") != -1 and len(dmesg_lines) > i+3:
510                                 self.devicetext = dmesg_lines[i+1].lstrip()+"\n"+dmesg_lines[i+3]
511                         elif line.find("/dev/scsi/host") != -1:
512                                 self.stickdevice = line.split(":",1)[0].lstrip()
513
514                 if retval != 0 or self.devicetext is None or self.stickdevice is None:
515                         self.session.openWithCallback(self.remove_img, MessageBox, _("No useable USB stick found"), MessageBox.TYPE_ERROR)
516                 else:
517                         self.session.openWithCallback(self.fdisk_query, MessageBox, (_("The following device was found:\n\n%s\n\nDo you want to write the USB flasher to this stick?") % self.devicetext), MessageBox.TYPE_YESNO)
518
519         def fdisk_query(self, answer):
520                 if answer == True and self.stickdevice:
521                         self["statusbar"].text = ("Partitioning USB stick...")
522                         self["job_progressbar"].range = 1000
523                         self["job_progressbar"].value = 100
524                         self["job_progresslabel"].text = "5.00%"
525                         self.taskstring = ""
526                         self.container.appClosed.append(self.fdisk_finished)
527                         self.container.execute("fdisk " + self.stickdevice + "/disc")
528                         self.container.write("d\n4\nd\n3\nd\n2\nd\nn\np\n1\n\n\nt\n6\nw\n")
529                         self.delayTimer = eTimer()
530                         self.delayTimer.callback.append(self.progress_increment)
531                         self.delayTimer.start(105, False)
532                 else:
533                         self.remove_img(True)
534
535         def fdisk_finished(self, retval):
536                 self.container.appClosed.remove(self.fdisk_finished)
537                 self.delayTimer.stop()
538                 if retval == 0:
539                         if fileExists(self.imagefilename):
540                                 self.tar_finished(0)
541                                 self["job_progressbar"].value = 700
542                         else:
543                                 self["statusbar"].text = ("Decompressing USB stick flasher boot image...")
544                                 self.taskstring = ""
545                                 self.container.appClosed.append(self.tar_finished)
546                                 self.container.setCWD("/tmp")
547                                 self.cmd = "tar -xjvf nfiflasher_image.tar.bz2"
548                                 self.container.execute(self.cmd)
549                                 print "executing " + self.cmd
550                                 self.delayTimer = eTimer()
551                                 self.delayTimer.callback.append(self.progress_increment)
552                                 self.delayTimer.start(105, False)
553                 else:
554                         print "fdisk failed: " + str(retval)
555                         self.session.openWithCallback(self.remove_img, MessageBox, ("fdisk " + _("failed") + ":\n" + str(self.taskstring)), MessageBox.TYPE_ERROR)
556
557         def progress_increment(self):
558                 newval = int(self["job_progressbar"].value) + 1
559                 if newval < 950:
560                         self["job_progressbar"].value = newval
561                         self["job_progresslabel"].text = "%.2f%%" % (newval/10.0)
562
563         def tar_finished(self, retval):
564                 self.delayTimer.stop()
565                 if len(self.container.appClosed) > 0:
566                         self.container.appClosed.remove(self.tar_finished)
567                 if retval == 0:
568                         self.imagefilename = "/tmp/nfiflash_" + self.box + ".img"
569                         self["statusbar"].text = ("Copying USB flasher boot image to stick...")
570                         self.taskstring = ""
571                         self.container.appClosed.append(self.dd_finished)
572                         self.cmd = "dd if=%s of=%s" % (self.imagefilename,self.stickdevice+"/part1")
573                         self.container.execute(self.cmd)
574                         print "executing " + self.cmd
575                         self.delayTimer = eTimer()
576                         self.delayTimer.callback.append(self.progress_increment)
577                         self.delayTimer.start(105, False)
578                 else:
579                         self.session.openWithCallback(self.remove_img, MessageBox, (self.cmd + " " + _("failed") + ":\n" + str(self.taskstring)), MessageBox.TYPE_ERROR)
580
581         def dd_finished(self, retval):
582                 self.delayTimer.stop()
583                 self.container.appClosed.remove(self.dd_finished)
584                 self.downloading(False)
585                 if retval == 0:
586                         self["job_progressbar"].value = 950
587                         self["job_progresslabel"].text = "95.00%"
588                         self["statusbar"].text = ("Remounting stick partition...")
589                         self.taskstring = ""
590                         self.container.appClosed.append(self.mount_finished)
591                         self.cmd = "mount %s /mnt/usb -o rw,sync" % (self.stickdevice+"/part1")
592                         self.container.execute(self.cmd)
593                         print "executing " + self.cmd
594                 else:
595                         self.session.openWithCallback(self.remove_img, MessageBox, (self.cmd + " " + _("failed") + ":\n" + str(self.taskstring)), MessageBox.TYPE_ERROR)
596
597         def mount_finished(self, retval):
598                 self.container.dataAvail.remove(self.tool_avail)
599                 self.container.appClosed.remove(self.mount_finished)
600                 if retval == 0:
601                         self["job_progressbar"].value = 1000
602                         self["job_progresslabel"].text = "100.00%"
603                         self["statusbar"].text = (".NFI Flasher bootable USB stick successfully created.")
604                         self.session.openWithCallback(self.flasherFinishedCB, MessageBox, _("The USB stick is now bootable. Do you want to download the latest image from the feed server and save it on the stick?"), type = MessageBox.TYPE_YESNO)
605                         self["destlist"].changeDir("/mnt/usb")
606                 else:
607                         self.session.openWithCallback(self.flasherFinishedCB, MessageBox, (self.cmd + " " + _("failed") + ":\n" + str(self.taskstring)), MessageBox.TYPE_ERROR)
608                         self.remove_img(True)
609
610         def remove_img(self, answer):
611                 if fileExists("/tmp/nfiflasher_image.tar.bz2"):
612                         remove("/tmp/nfiflasher_image.tar.bz2")
613                 if fileExists(self.imagefilename):
614                         remove(self.imagefilename)
615                 self.downloading(False)
616                 self.switchList(self.LIST_SOURCE)
617
618         def flasherFinishedCB(self, answer):
619                 if answer == True:
620                         self.wizard_mode = True
621                         self["feedlist"].moveSelection(0)
622                         self["path_bottom"].text = str(self["destlist"].getCurrentDirectory())
623                         self.nfo_download()
624                         self.nfi_download()
625
626         def configBackup(self):
627                 self.session.openWithCallback(self.runBackup, MessageBox, _("The wizard can backup your current settings. Do you want to do a backup now?"))
628
629         def runBackup(self, result=None):
630                 from Tools.Directories import createDir, isMount, pathExists
631                 from time import localtime
632                 from datetime import date
633                 from Screens.Console import Console
634                 if result:
635                         if isMount("/mnt/usb/"):
636                                 if (pathExists("/mnt/usb/backup") == False):
637                                         createDir("/mnt/usb/backup", True)
638                                 d = localtime()
639                                 dt = date(d.tm_year, d.tm_mon, d.tm_mday)
640                                 self.backup_file = "backup/" + str(dt) + "_settings_backup.tar.gz"
641                                 self.session.open(Console, title = "Backup running", cmdlist = ["tar -czvf " + "/mnt/usb/" + self.backup_file + " /etc/enigma2/ /etc/network/interfaces /etc/wpa_supplicant.conf"], finishedCallback = self.backup_finished, closeOnSuccess = True)
642                 else:
643                         self.backup_file = None
644                         self.backup_finished(skipped=True)
645
646         def backup_finished(self, skipped=False):
647                 if not skipped:
648                         wizardfd = open("/mnt/usb/wizard.nfo", "w")
649                         if wizardfd:
650                                 wizardfd.write("image: "+self["feedlist"].getNFIname()+'\n')
651                                 wizardfd.write("configuration: "+self.backup_file+'\n')
652                                 wizardfd.close()
653                 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)
654
655         def closeCB(self):
656                 if self.download:
657                         self.download.stop()
658                         #self.nfi_failed(None, "Cancelled by user request")
659                         self.downloading(False)
660                 else:
661                         self.close()
662
663 def main(session, **kwargs):
664         session.open(NFIDownload,"/home/root")
665
666 def filescan_open(list, session, **kwargs):
667         dev = "/dev/" + (list[0].path).rsplit('/',1)[0][7:]
668         print "mounting device " + dev + " to /mnt/usb..."
669         system("mount "+dev+" /mnt/usb/ -o rw,sync")
670         session.open(NFIDownload,"/mnt/usb/")
671
672 def filescan(**kwargs):
673         from Components.Scanner import Scanner, ScanPath
674         return \
675                 Scanner(mimetypes = ["application/x-dream-image"], 
676                         paths_to_scan = 
677                                 [
678                                         ScanPath(path = "", with_subdirs = False),
679                                 ], 
680                         name = "NFI", 
681                         description = (_("Download .NFI-Files for USB-Flasher")+"..."),
682                         openfnc = filescan_open, )