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