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