036523a4065e0db770aeb0cdf0bdcf8451812282
[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                         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, )