remove weird charset specifiers
[enigma2.git] / 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
22 class UserRequestedCancel(Exception):
23         pass
24
25 class HTTPProgressDownloader(client.HTTPDownloader):
26         def __init__(self, url, outfile, headers=None):
27                 client.HTTPDownloader.__init__(self, url, outfile, headers=headers, agent="Dreambox .NFI Download Plugin")
28                 self.status = None
29                 self.progress_callback = None
30                 self.deferred = defer.Deferred()
31
32         def noPage(self, reason):
33                 if self.status == "304":
34                         print reason.getErrorMessage()
35                         client.HTTPDownloader.page(self, "")
36                 else:
37                         client.HTTPDownloader.noPage(self, reason)
38         
39         def gotHeaders(self, headers):
40                 if self.status == "200":
41                         if headers.has_key("content-length"):
42                                 self.totalbytes = int(headers["content-length"][0])
43                         else:
44                                 self.totalbytes = 0
45                         self.currentbytes = 0.0
46                 return client.HTTPDownloader.gotHeaders(self, headers)
47         
48         def pagePart(self, packet):
49                 if self.status == "200":
50                         self.currentbytes += len(packet)
51                 if self.totalbytes and self.progress_callback:
52                         self.progress_callback(self.currentbytes, self.totalbytes)
53                 return client.HTTPDownloader.pagePart(self, packet)
54         
55         def pageEnd(self):
56                 return client.HTTPDownloader.pageEnd(self)
57
58 class downloadWithProgress:
59         def __init__(self, url, outputfile, contextFactory=None, *args, **kwargs):
60                 scheme, host, port, path = client._parse(url)
61                 self.factory = HTTPProgressDownloader(url, outputfile, *args, **kwargs)
62                 self.connection = reactor.connectTCP(host, port, self.factory)
63         
64         def start(self):
65                 return self.factory.deferred
66         
67         def stop(self):
68                 print "[stop]"
69                 self.connection.disconnect()
70                 #self.factory.deferred.errback(failure.Failure(UserRequestedCancel))
71
72         def addProgress(self, progress_callback):
73                 print "[addProgress]"
74                 self.factory.progress_callback = progress_callback
75
76 class Feedlist(MenuList):
77         def __init__(self, list=[], enableWrapAround = False):
78                 MenuList.__init__(self, list, enableWrapAround, eListboxPythonMultiContent)
79                 self.l.setFont(0, gFont("Regular", 16))
80                 self.l.setItemHeight(22)
81
82         def clear(self):
83                 del self.list[:]
84                 self.l.setList(self.list)
85
86         def getNFIname(self):
87                 l = self.l.getCurrentSelection()
88                 return l and l[0][0]
89         
90         def getNFIurl(self):
91                 l = self.l.getCurrentSelection()
92                 return l and l[0][1]
93         
94         def getNFOname(self):
95                 l = self.l.getCurrentSelection()
96                 return l and l[0][0][:-3]+"nfo"
97
98         def getNFOurl(self):
99                 l = self.l.getCurrentSelection()
100                 return l and l[0][1][:-3]+"nfo"
101
102         def isValid(self):
103                 l = self.l.getCurrentSelection()
104                 if l[0] == 0:
105                         return False
106                 else:
107                         return True
108         
109         def moveSelection(self,idx=0):
110                 if self.instance is not None:
111                         self.instance.moveSelectionTo(idx)
112
113 class NFIDownload(Screen):
114         LIST_SOURCE = 1
115         LIST_DEST = 2
116         skin = """
117                 <screen name="NFIDownload" position="90,95" size="560,420" title="Image download utility">
118                         <ePixmap pixmap="skin_default/buttons/red.png" position="0,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
119                         <ePixmap pixmap="skin_default/buttons/green.png" position="140,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
120                         <ePixmap pixmap="skin_default/buttons/yellow.png" position="280,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
121                         <ePixmap pixmap="skin_default/buttons/blue.png" position="420,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
122                         <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" />
123                         <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" />
124                         <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" />
125                         <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" />
126                         
127                         <widget source="label_top" render="Label" position="10,44" size="240,20" font="Regular;16" />
128                         <widget name="feedlist" position="10,66" size="250,222" scrollbarMode="showOnDemand" />
129                         <widget name="destlist" position="0,66" size="260,222" scrollbarMode="showOnDemand" />
130
131                         <widget source="label_bottom" render="Label" position="10,312" size="240,18" font="Regular;16"/>
132                         <widget source="path_bottom" render="Label" position="10,330" size="250,42" font="Regular;18" />
133                         
134                         <widget source="infolabel" render="Label" position="270,44" size="280,284" font="Regular;16" />
135                         <widget source="job_progressbar" render="Progress" position="10,374" size="540,26" borderWidth="1" backgroundColor="#254f7497" />
136                         <widget source="job_progresslabel" render="Label" position="130,378" zPosition="2" font="Regular;18" halign="center" transparent="1" size="300,22" foregroundColor="#000000" />
137                         <widget source="statusbar" render="Label" position="10,404" size="540,16" font="Regular;16" foregroundColor="#cccccc" />
138                 </screen>"""
139
140         def __init__(self, session, destdir="/tmp/"):
141                 self.skin = NFIDownload.skin
142                 Screen.__init__(self, session)
143                 
144                 self["job_progressbar"] = Progress()
145                 self["job_progresslabel"] = StaticText()
146                 
147                 self["infolabel"] = StaticText()
148                 self["statusbar"] = StaticText()
149                 self["label_top"] = StaticText()
150                 self["label_bottom"] = StaticText()
151                 self["path_bottom"] = StaticText()
152                 
153                 self["key_green"] = StaticText()
154                 self["key_yellow"] = StaticText()
155                 self["key_blue"] = StaticText()
156
157                 self["key_red"] = StaticText()
158
159                 self["feedlist"] = Feedlist([0,(eListboxPythonMultiContent.TYPE_TEXT, 0, 0,250, 30, 0, RT_HALIGN_LEFT|RT_VALIGN_CENTER, "feed not available")])
160                 self["destlist"] = FileList(destdir, showDirectories = True, showFiles = False)
161                 self["destlist"].hide()
162
163                 self.download_container = eConsoleAppContainer()
164                 self.nfo = ""
165                 self.nfofile = ""
166                 self.feedhtml = ""
167                 self.focus = None
168                 self.download = None
169                 self.box = HardwareInfo().get_device_name()
170                 self.feed_base = "http://www.dreamboxupdate.com/opendreambox/1.5/%s/images/" % self.box
171                 self.nfi_filter = "" # "release" # only show NFIs containing this string, or all if ""
172                 
173                 self["actions"] = ActionMap(["OkCancelActions", "ColorActions", "DirectionActions", "EPGSelectActions"],
174                 {
175                         "cancel": self.closeCB,
176                         "red": self.closeCB,
177                         "green": self.nfi_download,
178                         "yellow": self.switchList,
179                         "blue": self.askCreateUSBstick,
180                         "prevBouquet": self.switchList,
181                         "nextBouquet": self.switchList,
182                         "ok": self.ok,
183                         "left": self.left,
184                         "right": self.right,
185                         "up": self.up,
186                         "upRepeated": self.up,
187                         "downRepeated": self.down,
188                         "down": self.down
189                 }, -1)
190                 
191                 self.feed_download()
192         
193         def downloading(self, state=True):
194                 if state is True:       
195                         self["key_red"].text = _("Cancel")
196                         self["key_green"].text = ""
197                         self["key_yellow"].text = ""
198                         self["key_blue"].text = ""
199                         
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                         localfile = self["destlist"].getCurrentDirectory()+'/'+self["feedlist"].getNFIname()
367                         print "[nfi_download] downloading %s to %s" % (url, localfile)
368                         self.download = downloadWithProgress(url,localfile)
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_failed(self, failure_instance):
392                 #print "[nfi_failed] " 
393                 #print failure_instance
394                 #if isinstance(failure_instance, Plugins.SystemPlugins.NFIFlash.plugin.UserRequestedCancel):
395                         #print "is instance of Plugins.SystemPlugins.NFIFlash.plugin.UserRequestedCancel"
396                 #else:
397                         #print "not an instance of Plugins.SystemPlugins.NFIFlash.plugin.UserRequestedCancel"
398
399         def nfi_finished(self, string=""):
400                 print "[nfi_finished] " + str(string)
401                 if self.nfo != "":
402                         self.nfofilename = self["destlist"].getCurrentDirectory() + '/' + self["feedlist"].getNFOname()
403                         nfofd = open(self.nfofilename, "w")
404                         if nfofd:
405                                 nfofd.write(self.nfo)
406                                 nfofd.close()
407                         else:
408                                 print "couldn't save nfo file " + self.nfofilename
409         
410                         pos = self.nfo.find("md5sum")
411                         if pos > 0:                                     
412                                 self["statusbar"].text = _("Please wait for md5 signature verification...")
413                                 cmd = "md5sum -cs " + self.nfofilename
414                                 print cmd
415                                 self.download_container.setCWD(self["destlist"].getCurrentDirectory())
416                                 self.download_container.appClosed.get().append(self.md5finished)
417                                 self.download_container.execute(cmd)
418                         else:
419                                 self["statusbar"].text = "Download completed."
420                                 self.downloading(False)
421                 else:
422                         self["statusbar"].text = "Download completed."
423                         self.downloading(False)
424
425         def md5finished(self, retval):
426                 print "[md5finished]: " + str(retval)
427                 self.download_container.appClosed.get().remove(self.md5finished)
428                 if retval==0:
429                         self["statusbar"].text = _(".NFI file passed md5sum signature check. You can safely flash this image!")
430                         self.switchList(self.LIST_SOURCE)
431                         self.downloading(False)
432                 else:
433                         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)
434         
435         def nfi_remove(self, answer):
436                 self.downloading(False)
437                 if answer == True:
438                         nfifilename =  self["destlist"].getCurrentDirectory()+'/'+self["feedlist"].getNFIname()
439                         if fileExists(self.nfofilename):
440                                 remove(self.nfofilename)
441                         if fileExists(nfifilename):
442                                 remove(nfifilename)
443                 self.switchList(self.LIST_SOURCE)
444
445         def askCreateUSBstick(self):
446                 self.downloading()
447                 self.imagefilename = "/tmp/nfiflash_" + self.box + ".img"
448                 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.")
449                 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)
450                 
451         def flasherdownload_query(self, answer):
452                 if answer is False:
453                         self.downloading(False)
454                         self.switchList(self.LIST_SOURCE)
455                         return
456                 #url = self.feed_base + "/nfiflasher_" + self.box + ".tar.bz2"
457                 url = "http://www.dreamboxupdate.com/download/opendreambox/dreambox-nfiflasher-%s.tar.bz2" % self.box
458                 localfile = "/tmp/nfiflasher_image.tar.bz2"
459                 print "[flasherdownload_query] downloading %s to %s" % (url, localfile)
460                 self["statusbar"].text = ("Downloading %s..." % url)
461                 self.download = downloadWithProgress(url,localfile)
462                 self.download.addProgress(self.nfi_progress)
463                 self["job_progressbar"].range = 1000
464                 self.download.start().addCallback(self.flasherdownload_finished).addErrback(self.flasherdownload_failed)
465
466         def flasherdownload_failed(self, failure_instance=None, error_message=""):
467                 if error_message == "" and failure_instance is not None:
468                         error_message = failure_instance.getErrorMessage()
469                 print "[flasherdownload_failed] " + error_message
470                 message = "%s %s" % (_("Download of USB flasher boot image failed: "),error_message)
471                 self.session.open(MessageBox, message, MessageBox.TYPE_ERROR)
472                 self.remove_img(True)
473
474         def flasherdownload_finished(self, string=""):
475                 print "[flasherdownload_finished] " + str(string)       
476                 self.container = eConsoleAppContainer()
477                 self.container.appClosed.get().append(self.umount_finished)
478                 self.container.dataAvail.get().append(self.tool_avail)
479                 self.taskstring = ""
480                 umountdevs = ""
481                 from os import listdir
482                 for device in listdir("/dev"):
483                         if device[:2] == "sd" and device[-1:].isdigit():
484                                 umountdevs += "/dev/"+device
485                 self.cmd = "umount " + umountdevs
486                 print "executing " + self.cmd
487                 self.container.execute(self.cmd)
488
489         def tool_avail(self, string):
490                 print "[tool_avail]" + string
491                 self.taskstring += string
492
493         def umount_finished(self, retval):
494                 self.container.appClosed.get().remove(self.umount_finished)
495                 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)
496
497         def dmesg_clear(self, answer):
498                 self.container.appClosed.get().append(self.dmesg_cleared)
499                 self.taskstring = ""
500                 self.cmd = "dmesg -c"
501                 print "executing " + self.cmd
502                 self.container.execute(self.cmd)
503                 
504         def dmesg_cleared(self, retval):
505                 self.container.appClosed.get().remove(self.dmesg_cleared)
506                 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)
507
508         def stick_back_in(self, answer):
509                 self["statusbar"].text = _("Waiting for USB stick to settle...")
510                 self.delayTimer = eTimer()
511                 self.delayTimer.callback.append(self.waiting_for_stick)
512                 self.delayCount = -1
513                 self.delayTimer.start(1000)
514
515         def waiting_for_stick(self):
516                 self.delayCount += 1
517                 self["job_progressbar"].range = 6
518                 self["job_progressbar"].value = self.delayCount
519                 self["job_progresslabel"].text = "-%d s" % (6-self.delayCount)
520                 if self.delayCount > 5:
521                         self.delayTimer.stop()
522                         self.container.appClosed.get().append(self.dmesg_scanned)
523                         self.taskstring = ""
524                         self.cmd = "dmesg"
525                         print "executing " + self.cmd
526                         self.container.execute(self.cmd)
527                 
528         def dmesg_scanned(self, retval):
529                 self.container.appClosed.get().remove(self.dmesg_scanned)
530                 dmesg_lines = self.taskstring.splitlines()
531                 self.devicetext = None
532                 self.stickdevice = None
533                 for i, line in enumerate(dmesg_lines):
534                         if line.find("usb-storage: waiting for device") != -1 and len(dmesg_lines) > i+3:
535                                 self.devicetext = dmesg_lines[i+1].lstrip()+"\n"+dmesg_lines[i+3]
536                         elif line.find("/dev/scsi/host") != -1:
537                                 self.stickdevice = line.split(":",1)[0].lstrip()
538
539                 if retval != 0 or self.devicetext is None or self.stickdevice is None:
540                         self.session.openWithCallback(self.remove_img, MessageBox, _("No useable USB stick found"), MessageBox.TYPE_ERROR)
541                 else:
542                         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)
543         
544         def fdisk_query(self, answer):
545                 if answer == True:
546                         self["statusbar"].text = _("Partitioning USB stick...")
547                         self["job_progressbar"].range = 1000
548                         self["job_progressbar"].value = 100
549                         self["job_progresslabel"].text = "5.00%"
550                         self.taskstring = ""
551                         self.container.appClosed.get().append(self.fdisk_finished)
552                         self.container.execute("fdisk " + self.stickdevice + "/disc")
553                         self.container.write("d\nn\np\n1\n\n\nt\n6\nw\n")
554                         self.delayTimer = eTimer()
555                         self.delayTimer.callback.append(self.progress_increment)
556                         self.delayTimer.start(105, False)
557                 else:
558                         self.remove_img(True)
559
560         def fdisk_finished(self, retval):
561                 self.container.appClosed.get().remove(self.fdisk_finished)
562                 self.delayTimer.stop()
563                 if retval == 0:
564                         if fileExists(self.imagefilename):
565                                 self.tar_finished(0)
566                                 self["job_progressbar"].value = 700
567                         else:
568                                 self["statusbar"].text = _("Decompressing USB stick flasher boot image...")
569                                 self.taskstring = ""
570                                 self.container.appClosed.get().append(self.tar_finished)
571                                 self.container.setCWD("/tmp")
572                                 self.cmd = "tar -xjvf nfiflasher_image.tar.bz2"
573                                 self.container.execute(self.cmd)
574                                 print "executing " + self.cmd
575                                 self.delayTimer = eTimer()
576                                 self.delayTimer.callback.append(self.progress_increment)
577                                 self.delayTimer.start(105, False)
578                 else:
579                         print "fdisk failed: " + str(retval)
580                         self.session.openWithCallback(self.remove_img, MessageBox, ("fdisk " + _("failed") + ":\n" + str(self.taskstring)), MessageBox.TYPE_ERROR)
581
582         def progress_increment(self):
583                 newval = int(self["job_progressbar"].value) + 1
584                 if newval < 950:
585                         self["job_progressbar"].value = newval
586                         self["job_progresslabel"].text = "%.2f%%" % (newval/10.0)
587
588         def tar_finished(self, retval):
589                 self.delayTimer.stop()
590                 if len(self.container.appClosed.get()) > 0:
591                         self.container.appClosed.get().remove(self.tar_finished)
592                 if retval == 0:
593                         self.imagefilename = "/tmp/nfiflash_" + self.box + ".img"
594                         self["statusbar"].text = _("Copying USB flasher boot image to stick...")
595                         self.taskstring = ""
596                         self.container.appClosed.get().append(self.dd_finished)
597                         self.cmd = "dd if=%s of=%s" % (self.imagefilename,self.stickdevice+"/part1")
598                         self.container.execute(self.cmd)
599                         print "executing " + self.cmd
600                         self.delayTimer = eTimer()
601                         self.delayTimer.callback.append(self.progress_increment)
602                         self.delayTimer.start(105, False)
603                 else:
604                         self.session.openWithCallback(self.remove_img, MessageBox, (self.cmd + " " + _("failed") + ":\n" + str(self.taskstring)), MessageBox.TYPE_ERROR)
605
606         def dd_finished(self, retval):
607                 self.delayTimer.stop()
608                 self.container.appClosed.get().remove(self.dd_finished)
609                 self.downloading(False)
610                 if retval == 0:
611                         self["job_progressbar"].value = 950
612                         self["job_progresslabel"].text = "95.00%"
613                         self["statusbar"].text = _("Remounting stick partition...")
614                         self.taskstring = ""
615                         self.container.appClosed.get().append(self.mount_finished)
616                         self.cmd = "mount %s /mnt/usb -o rw,sync" % (self.stickdevice+"/part1")
617                         self.container.execute(self.cmd)
618                         print "executing " + self.cmd
619                 else:
620                         self.session.openWithCallback(self.remove_img, MessageBox, (self.cmd + " " + _("failed") + ":\n" + str(self.taskstring)), MessageBox.TYPE_ERROR)
621
622         def mount_finished(self, retval):
623                 self.container.dataAvail.get().remove(self.tool_avail)
624                 self.container.appClosed.get().remove(self.mount_finished)
625                 if retval == 0:
626                         self["job_progressbar"].value = 1000
627                         self["job_progresslabel"].text = "100.00%"
628                         self["statusbar"].text = _(".NFI Flasher bootable USB stick successfully created.")
629                         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)
630                         self["destlist"].changeDir("/mnt/usb")
631                 else:
632                         self.session.openWithCallback(self.remove_img, MessageBox, (self.cmd + " " + _("failed") + ":\n" + str(self.taskstring)), MessageBox.TYPE_ERROR)
633         
634         def remove_img(self, answer):
635                 if fileExists("/tmp/nfiflasher_image.tar.bz2"):
636                         remove("/tmp/nfiflasher_image.tar.bz2")
637                 if fileExists(self.imagefilename):
638                         remove(self.imagefilename)
639                 self.downloading(False)
640                 self.switchList(self.LIST_SOURCE)
641
642         def closeCB(self):
643                 if self.download:
644                         self.download.stop()
645                         #self.nfi_failed(None, "Cancelled by user request")
646                         self.downloading(False)
647                 else:
648                         self.close()
649
650 def main(session, **kwargs):
651         session.open(NFIDownload,"/home/root")
652
653 def filescan_open(list, session, **kwargs):
654         dev = "/dev/" + (list[0].path).rsplit('/',1)[0][7:]
655         print "mounting device " + dev + " to /mnt/usb..."
656         system("mount "+dev+" /mnt/usb/ -o rw,sync")
657         session.open(NFIDownload,"/mnt/usb/")
658
659 def filescan(**kwargs):
660         from Components.Scanner import Scanner, ScanPath
661         return \
662                 Scanner(mimetypes = ["application/x-dream-image"], 
663                         paths_to_scan = 
664                                 [
665                                         ScanPath(path = "", with_subdirs = False), 
666                                 ], 
667                         name = "NFI", 
668                         description = (_("Download .NFI-Files for USB-Flasher")+"..."), 
669                         openfnc = filescan_open, )