fix md5 hash check
[enigma2.git] / lib / python / Plugins / SystemPlugins / NFIFlash / downloader.py
1 # -*- coding: utf8 -*-
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
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                         
201                 else:
202                         self.download = None
203                         self["key_red"].text = _("Exit")
204                         if self["feedlist"].isValid():
205                                 self["key_green"].text = (_("Download"))
206                                 if self.focus is self.LIST_SOURCE:
207                                         self["key_yellow"].text = (_("Change dir."))
208                                 else:
209                                         self["key_yellow"].text = (_("Select image"))
210                         self["key_blue"].text = (_("Fix USB stick"))
211                 
212         def switchList(self,to_where=None):
213                 if self.download or not self["feedlist"].isValid():
214                         return
215                 
216                 self["job_progressbar"].value = 0
217                 self["job_progresslabel"].text = ""
218                 
219                 if to_where is None:
220                         if self.focus is self.LIST_SOURCE:
221                                 to_where = self.LIST_DEST
222                         if self.focus is self.LIST_DEST:
223                                 to_where = self.LIST_SOURCE
224                                 
225                 if to_where is self.LIST_DEST:
226                         self.focus = self.LIST_DEST
227                         self["statusbar"].text = _("Please select target directory or medium")
228                         self["label_top"].text = _("choose destination directory")+":"
229                         self["feedlist"].hide()
230                         self["destlist"].show()
231                         self["label_bottom"].text = _("Selected source image")+":"
232                         self["path_bottom"].text = str(self["feedlist"].getNFIname())
233                         self["key_yellow"].text = (_("Select image"))
234                 
235                 elif to_where is self.LIST_SOURCE:
236                         self.focus = self.LIST_SOURCE
237                         self["statusbar"].text = _("Please choose .NFI image file from feed server to download")
238                         self["label_top"].text = _("select image from server")+":"
239                         self["feedlist"].show()
240                         self["destlist"].hide()
241                         self["label_bottom"].text = _("Destination directory")+":"
242                         self["path_bottom"].text = str(self["destlist"].getCurrentDirectory())
243                         self["key_yellow"].text = (_("Change dir."))
244                         
245         def up(self):
246                 if self.download:
247                         return
248                 if self.focus is self.LIST_SOURCE:
249                         self["feedlist"].up()
250                         self.nfo_download()
251                 if self.focus is self.LIST_DEST:
252                         self["destlist"].up()
253         
254         def down(self):
255                 if self.download:
256                         return
257                 if self.focus is self.LIST_SOURCE:
258                         self["feedlist"].down()
259                         self.nfo_download()
260                 if self.focus is self.LIST_DEST:
261                         self["destlist"].down()
262                         
263         def left(self):
264                 if self.download:
265                         return
266                 if self.focus is self.LIST_SOURCE:
267                         self["feedlist"].pageUp()
268                         self.nfo_download()
269                 if self.focus is self.LIST_DEST:
270                         self["destlist"].pageUp()
271         
272         def right(self):
273                 if self.download:
274                         return
275                 if self.focus is self.LIST_SOURCE:
276                         self["feedlist"].pageDown()
277                         self.nfo_download()
278                 if self.focus is self.LIST_DEST:
279                         self["destlist"].pageDown()
280
281         def ok(self):
282                 if self.download:
283                         return
284                 if self.focus is self.LIST_DEST:
285                         if self["destlist"].canDescent():
286                                 self["destlist"].descent()
287
288         def feed_download(self):
289                 self.downloading(True)
290                 self.download = self.feed_download
291                 client.getPage(self.feed_base).addCallback(self.feed_finished).addErrback(self.feed_failed)
292
293         def feed_failed(self, failure_instance):
294                 print "[feed_failed] " + str(failure_instance)
295                 self["infolabel"].text = _("Could not connect to Dreambox .NFI Image Feed Server:") + "\n" + failure_instance.getErrorMessage() + "\n\n" + _("Please check your network settings!")
296                 self.downloading(False)
297
298         def feed_finished(self, feedhtml):
299                 print "[feed_finished] " + str(feedhtml)
300                 self.downloading(False)
301                 fileresultmask = re.compile("<a href=[\'\"](?P<url>.*?)[\'\"]>(?P<name>.*?.nfi)</a>", re.DOTALL)
302                 searchresults = fileresultmask.finditer(feedhtml)
303                 fileresultlist = []
304                 if searchresults:
305                         for x in searchresults:
306                                 url = x.group("url")
307                                 if url[0:7] != "http://":
308                                         url = self.feed_base + x.group("url")
309                                 name = x.group("name")
310                                 if name.find(self.nfi_filter) > -1:
311                                         entry = [[name, url],(eListboxPythonMultiContent.TYPE_TEXT, 0, 0,250, 30, 0, RT_HALIGN_LEFT|RT_VALIGN_CENTER, name)]
312                                         print "adding to feedlist: " + str(entry)
313                                         fileresultlist.append(entry)
314                                 else:
315                                         print "NOT adding to feedlist: " + name
316                         self["feedlist"].l.setList(fileresultlist)
317                         self["feedlist"].moveSelection(0)
318
319                 if len(fileresultlist) > 0:
320                         self.switchList(self.LIST_SOURCE)
321                         self.nfo_download()
322                 else:
323                         self["infolabel"].text = _("Cannot parse feed directory")
324
325         def nfo_download(self):
326                 print "[check_for_NFO]"
327                 if self["feedlist"].isValid():
328                         print "nfiname: " + self["feedlist"].getNFIname()
329                         self["job_progressbar"].value = 0
330                         self["job_progresslabel"].text = ""
331                         if self["feedlist"].getNFIurl() is None:
332                                 self["key_green"].text = ""
333                                 return
334                         self["key_green"].text = _("Download")
335                         nfourl = self["feedlist"].getNFOurl()
336                         print "downloading " + nfourl
337                         self.download = self.nfo_download
338                         self.downloading(True)
339                         client.getPage(nfourl).addCallback(self.nfo_finished).addErrback(self.nfo_failed)
340                         self["statusbar"].text = _("Downloading image description...")
341
342         def nfo_failed(self, failure_instance):
343                 print "[nfo_failed] " + str(failure_instance)
344                 self["infolabel"].text = _("No details for this image file") + "\n" + self["feedlist"].getNFIname()
345                 self["statusbar"].text = ""
346                 self.nfofilename = ""
347                 self.nfo = ""
348                 self.downloading(False)
349         
350         def nfo_finished(self,nfodata=""):
351                 print "[nfo_finished] " + str(nfodata)
352                 self.downloading(False)
353                 self.nfo = nfodata
354                 if self.nfo != "":
355                         self.nfofilename = self["destlist"].getCurrentDirectory() + '/' + self["feedlist"].getNFOname()
356                         self["infolabel"].text = self.nfo
357                 else:   
358                         self.nfofilename = ""
359                         self["infolabel"].text = _("No details for this image file")
360                 self["statusbar"].text = ""
361
362         def nfi_download(self):
363                 if self["destlist"].getCurrentDirectory() is None:
364                         self.switchList(self.LIST_TARGET)
365                 if self["feedlist"].isValid():
366                         url = self["feedlist"].getNFIurl()
367                         localfile = self["destlist"].getCurrentDirectory()+'/'+self["feedlist"].getNFIname()
368                         print "[nfi_download] downloading %s to %s" % (url, localfile)
369                         self.download = downloadWithProgress(url,localfile)
370                         self.download.addProgress(self.nfi_progress)
371                         self["job_progressbar"].range = 1000
372                         self.download.start().addCallback(self.nfi_finished).addErrback(self.nfi_failed)
373                         self.downloading(True)
374
375         def nfi_progress(self, recvbytes, totalbytes):
376                 #print "[update_progress] recvbytes=%d, totalbytes=%d" % (recvbytes, totalbytes)
377                 self["job_progressbar"].value = int(1000*recvbytes/float(totalbytes))
378                 self["job_progresslabel"].text = "%d of %d kBytes (%.2f%%)" % (recvbytes/1024, totalbytes/1024, 100*recvbytes/float(totalbytes))
379         
380         def nfi_failed(self, failure_instance=None, error_message=""):
381                 if error_message == "" and failure_instance is not None:
382                         error_message = failure_instance.getErrorMessage()
383                 print "[nfi_failed] " + error_message
384                 if fileExists(self["destlist"].getCurrentDirectory()+'/'+self["feedlist"].getNFIname()):
385                         message = "%s %s\n%s" % (_(".NFI Download failed:"), error_message, _("Remove the incomplete .NFI file?"))
386                         self.session.openWithCallback(self.nfi_remove, MessageBox, message, MessageBox.TYPE_YESNO)
387                 else:
388                         message = "%s %s" % (_(".NFI Download failed:"),error_message)
389                         self.session.open(MessageBox, message, MessageBox.TYPE_ERROR)
390                         self.downloading(False)
391
392         #def nfi_failed(self, failure_instance):
393                 #print "[nfi_failed] " 
394                 #print failure_instance
395                 #if isinstance(failure_instance, Plugins.SystemPlugins.NFIFlash.plugin.UserRequestedCancel):
396                         #print "is instance of Plugins.SystemPlugins.NFIFlash.plugin.UserRequestedCancel"
397                 #else:
398                         #print "not an instance of Plugins.SystemPlugins.NFIFlash.plugin.UserRequestedCancel"
399
400         def nfi_finished(self, string=""):
401                 print "[nfi_finished] " + str(string)
402                 if self.nfo != "":
403                         self.nfofilename = self["destlist"].getCurrentDirectory() + '/' + self["feedlist"].getNFOname()
404                         nfofd = open(self.nfofilename, "w")
405                         if nfofd:
406                                 nfofd.write(self.nfo)
407                                 nfofd.close()
408                         else:
409                                 print "couldn't save nfo file " + self.nfofilename
410         
411                         pos = self.nfo.find("md5sum")
412                         if pos > 0:                                     
413                                 self["statusbar"].text = _("Please wait for md5 signature verification...")
414                                 cmd = "md5sum -cs " + self.nfofilename
415                                 print cmd
416                                 self.download_container.setCWD(self["destlist"].getCurrentDirectory())
417                                 self.download_container.appClosed.get().append(self.md5finished)
418                                 self.download_container.execute(cmd)
419                         else:
420                                 self["statusbar"].text = "Download completed."
421                                 self.downloading(False)
422                 else:
423                         self["statusbar"].text = "Download completed."
424                         self.downloading(False)
425
426         def md5finished(self, retval):
427                 print "[md5finished]: " + str(retval)
428                 self.download_container.appClosed.get().remove(self.md5finished)
429                 if retval==0:
430                         self["statusbar"].text = _(".NFI file passed md5sum signature check. You can safely flash this image!")
431                         self.switchList(self.LIST_SOURCE)
432                         self.downloading(False)
433                 else:
434                         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)
435         
436         def nfi_remove(self, answer):
437                 self.downloading(False)
438                 if answer == True:
439                         nfifilename =  self["destlist"].getCurrentDirectory()+'/'+self["feedlist"].getNFIname()
440                         if fileExists(self.nfofilename):
441                                 remove(self.nfofilename)
442                         if fileExists(nfifilename):
443                                 remove(nfifilename)
444                 self.switchList(self.LIST_SOURCE)
445
446         def askCreateUSBstick(self):
447                 self.downloading()
448                 self.imagefilename = "/tmp/nfiflash_" + self.box + ".img"
449                 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.")
450                 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)
451                 
452         def flasherdownload_query(self, answer):
453                 if answer is False:
454                         self.downloading(False)
455                         self.switchList(self.LIST_SOURCE)
456                         return
457                 #url = self.feed_base + "/nfiflasher_" + self.box + ".tar.bz2"
458                 url = "http://www.dreamboxupdate.com/download/opendreambox/dreambox-nfiflasher-%s.tar.bz2" % self.box
459                 localfile = "/tmp/nfiflasher_image.tar.bz2"
460                 print "[flasherdownload_query] downloading %s to %s" % (url, localfile)
461                 self["statusbar"].text = ("Downloading %s..." % url)
462                 self.download = downloadWithProgress(url,localfile)
463                 self.download.addProgress(self.nfi_progress)
464                 self["job_progressbar"].range = 1000
465                 self.download.start().addCallback(self.flasherdownload_finished).addErrback(self.flasherdownload_failed)
466
467         def flasherdownload_failed(self, failure_instance=None, error_message=""):
468                 if error_message == "" and failure_instance is not None:
469                         error_message = failure_instance.getErrorMessage()
470                 print "[flasherdownload_failed] " + error_message
471                 message = "%s %s" % (_("Download of USB flasher boot image failed: "),error_message)
472                 self.session.open(MessageBox, message, MessageBox.TYPE_ERROR)
473                 self.remove_img(True)
474
475         def flasherdownload_finished(self, string=""):
476                 print "[flasherdownload_finished] " + str(string)       
477                 self.container = eConsoleAppContainer()
478                 self.container.appClosed.get().append(self.umount_finished)
479                 self.container.dataAvail.get().append(self.tool_avail)
480                 self.taskstring = ""
481                 umountdevs = ""
482                 from os import listdir
483                 for device in listdir("/dev"):
484                         if device[:2] == "sd" and device[-1:].isdigit():
485                                 umountdevs += "/dev/"+device
486                 self.cmd = "umount " + umountdevs
487                 print "executing " + self.cmd
488                 self.container.execute(self.cmd)
489
490         def tool_avail(self, string):
491                 print "[tool_avail]" + string
492                 self.taskstring += string
493
494         def umount_finished(self, retval):
495                 self.container.appClosed.get().remove(self.umount_finished)
496                 self.session.openWithCallback(self.dmesg_clear, MessageBox, _("To make sure you intend to do this, please remove the target USB stick now and stick it back in upon prompt. Press OK when you have taken the stick out."), MessageBox.TYPE_INFO)
497
498         def dmesg_clear(self, answer):
499                 self.container.appClosed.get().append(self.dmesg_cleared)
500                 self.taskstring = ""
501                 self.cmd = "dmesg -c"
502                 print "executing " + self.cmd
503                 self.container.execute(self.cmd)
504                 
505         def dmesg_cleared(self, retval):
506                 self.container.appClosed.get().remove(self.dmesg_cleared)
507                 self.session.openWithCallback(self.stick_back_in, MessageBox, (_("Now please insert the USB stick (minimum size is 64 MB) that you want to format and use as .NFI image flasher. Press OK after you've put the stick back in.")), MessageBox.TYPE_INFO)
508
509         def stick_back_in(self, answer):
510                 self["statusbar"].text = _("Waiting for USB stick to settle...")
511                 self.delayTimer = eTimer()
512                 self.delayTimer.callback.append(self.waiting_for_stick)
513                 self.delayCount = -1
514                 self.delayTimer.start(1000)
515
516         def waiting_for_stick(self):
517                 self.delayCount += 1
518                 self["job_progressbar"].range = 6
519                 self["job_progressbar"].value = self.delayCount
520                 self["job_progresslabel"].text = "-%d s" % (6-self.delayCount)
521                 if self.delayCount > 5:
522                         self.delayTimer.stop()
523                         self.container.appClosed.get().append(self.dmesg_scanned)
524                         self.taskstring = ""
525                         self.cmd = "dmesg"
526                         print "executing " + self.cmd
527                         self.container.execute(self.cmd)
528                 
529         def dmesg_scanned(self, retval):
530                 self.container.appClosed.get().remove(self.dmesg_scanned)
531                 dmesg_lines = self.taskstring.splitlines()
532                 self.devicetext = None
533                 self.stickdevice = None
534                 for i, line in enumerate(dmesg_lines):
535                         if line.find("usb-storage: waiting for device") != -1 and len(dmesg_lines) > i+3:
536                                 self.devicetext = dmesg_lines[i+1].lstrip()+"\n"+dmesg_lines[i+3]
537                         elif line.find("/dev/scsi/host") != -1:
538                                 self.stickdevice = line.split(":",1)[0].lstrip()
539
540                 if retval != 0 or self.devicetext is None or self.stickdevice is None:
541                         self.session.openWithCallback(self.remove_img, MessageBox, _("No useable USB stick found"), MessageBox.TYPE_ERROR)
542                 else:
543                         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)
544         
545         def fdisk_query(self, answer):
546                 if answer == True:
547                         self["statusbar"].text = _("Partitioning USB stick...")
548                         self["job_progressbar"].range = 1000
549                         self["job_progressbar"].value = 100
550                         self["job_progresslabel"].text = "5.00%"
551                         self.taskstring = ""
552                         self.container.appClosed.get().append(self.fdisk_finished)
553                         self.container.execute("fdisk " + self.stickdevice + "/disc")
554                         self.container.write("d\nn\np\n1\n\n\nt\n6\nw\n")
555                         self.delayTimer = eTimer()
556                         self.delayTimer.callback.append(self.progress_increment)
557                         self.delayTimer.start(105, False)
558                 else:
559                         self.remove_img(True)
560
561         def fdisk_finished(self, retval):
562                 self.container.appClosed.get().remove(self.fdisk_finished)
563                 self.delayTimer.stop()
564                 if retval == 0:
565                         if fileExists(self.imagefilename):
566                                 self.tar_finished(0)
567                                 self["job_progressbar"].value = 700
568                         else:
569                                 self["statusbar"].text = _("Decompressing USB stick flasher boot image...")
570                                 self.taskstring = ""
571                                 self.container.appClosed.get().append(self.tar_finished)
572                                 self.container.setCWD("/tmp")
573                                 self.cmd = "tar -xjvf nfiflasher_image.tar.bz2"
574                                 self.container.execute(self.cmd)
575                                 print "executing " + self.cmd
576                                 self.delayTimer = eTimer()
577                                 self.delayTimer.callback.append(self.progress_increment)
578                                 self.delayTimer.start(105, False)
579                 else:
580                         print "fdisk failed: " + str(retval)
581                         self.session.openWithCallback(self.remove_img, MessageBox, ("fdisk " + _("failed") + ":\n" + str(self.taskstring)), MessageBox.TYPE_ERROR)
582
583         def progress_increment(self):
584                 newval = int(self["job_progressbar"].value) + 1
585                 if newval < 950:
586                         self["job_progressbar"].value = newval
587                         self["job_progresslabel"].text = "%.2f%%" % (newval/10.0)
588
589         def tar_finished(self, retval):
590                 self.delayTimer.stop()
591                 if len(self.container.appClosed.get()) > 0:
592                         self.container.appClosed.get().remove(self.tar_finished)
593                 if retval == 0:
594                         self.imagefilename = "/tmp/nfiflash_" + self.box + ".img"
595                         self["statusbar"].text = _("Copying USB flasher boot image to stick...")
596                         self.taskstring = ""
597                         self.container.appClosed.get().append(self.dd_finished)
598                         self.cmd = "dd if=%s of=%s" % (self.imagefilename,self.stickdevice+"/part1")
599                         self.container.execute(self.cmd)
600                         print "executing " + self.cmd
601                         self.delayTimer = eTimer()
602                         self.delayTimer.callback.append(self.progress_increment)
603                         self.delayTimer.start(105, False)
604                 else:
605                         self.session.openWithCallback(self.remove_img, MessageBox, (self.cmd + " " + _("failed") + ":\n" + str(self.taskstring)), MessageBox.TYPE_ERROR)
606
607         def dd_finished(self, retval):
608                 self.delayTimer.stop()
609                 self.container.appClosed.get().remove(self.dd_finished)
610                 self.downloading(False)
611                 if retval == 0:
612                         self["job_progressbar"].value = 950
613                         self["job_progresslabel"].text = "95.00%"
614                         self["statusbar"].text = _("Remounting stick partition...")
615                         self.taskstring = ""
616                         self.container.appClosed.get().append(self.mount_finished)
617                         self.cmd = "mount %s /mnt/usb -o rw,sync" % (self.stickdevice+"/part1")
618                         self.container.execute(self.cmd)
619                         print "executing " + self.cmd
620                 else:
621                         self.session.openWithCallback(self.remove_img, MessageBox, (self.cmd + " " + _("failed") + ":\n" + str(self.taskstring)), MessageBox.TYPE_ERROR)
622
623         def mount_finished(self, retval):
624                 self.container.dataAvail.get().remove(self.tool_avail)
625                 self.container.appClosed.get().remove(self.mount_finished)
626                 if retval == 0:
627                         self["job_progressbar"].value = 1000
628                         self["job_progresslabel"].text = "100.00%"
629                         self["statusbar"].text = _(".NFI Flasher bootable USB stick successfully created.")
630                         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)
631                         self["destlist"].changeDir("/mnt/usb")
632                 else:
633                         self.session.openWithCallback(self.remove_img, MessageBox, (self.cmd + " " + _("failed") + ":\n" + str(self.taskstring)), MessageBox.TYPE_ERROR)
634         
635         def remove_img(self, answer):
636                 if fileExists("/tmp/nfiflasher_image.tar.bz2"):
637                         remove("/tmp/nfiflasher_image.tar.bz2")
638                 if fileExists(self.imagefilename):
639                         remove(self.imagefilename)
640                 self.downloading(False)
641                 self.switchList(self.LIST_SOURCE)
642
643         def closeCB(self):
644                 if self.download:
645                         self.download.stop()
646                         #self.nfi_failed(None, "Cancelled by user request")
647                         self.downloading(False)
648                 else:
649                         self.close()
650
651 def main(session, **kwargs):
652         session.open(NFIDownload,"/home/root")
653
654 def filescan_open(list, session, **kwargs):
655         dev = "/dev/" + (list[0].path).rsplit('/',1)[0][7:]
656         print "mounting device " + dev + " to /mnt/usb..."
657         system("mount "+dev+" /mnt/usb/ -o rw,sync")
658         session.open(NFIDownload,"/mnt/usb/")
659
660 def filescan(**kwargs):
661         from Components.Scanner import Scanner, ScanPath
662         return \
663                 Scanner(mimetypes = ["application/x-dream-image"], 
664                         paths_to_scan = 
665                                 [
666                                         ScanPath(path = "", with_subdirs = False), 
667                                 ], 
668                         name = "NFI", 
669                         description = (_("Download .NFI-Files for USB-Flasher")+"..."), 
670                         openfnc = filescan_open, )