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