whitespace cleanup,
[enigma2-plugins.git] / networkbrowser / src / NetworkBrowser.py
1 # -*- coding: utf-8 -*-
2 # for localized messages
3 from __init__ import _
4 from enigma import eTimer, getDesktop
5 from Screens.Screen import Screen
6 from Screens.MessageBox import MessageBox
7 from Components.Label import Label
8 from Components.ActionMap import ActionMap, NumberActionMap
9 from Components.Sources.List import List
10 from Components.Network import iNetwork
11 from Components.Input import Input
12 from Tools.Directories import resolveFilename, SCOPE_PLUGINS, SCOPE_SKIN_IMAGE
13 from Tools.LoadPixmap import LoadPixmap
14 from cPickle import dump, load
15 from os import path as os_path, stat, mkdir, remove
16 from time import time
17 from stat import ST_MTIME
18
19 import netscan
20 from MountManager import AutoMountManager
21 from AutoMount import iAutoMount
22 from MountEdit import AutoMountEdit
23 from UserDialog import UserDialog
24
25 def write_cache(cache_file, cache_data):
26         #Does a cPickle dump
27         if not os_path.isdir( os_path.dirname(cache_file) ):
28                 try:
29                         mkdir( os_path.dirname(cache_file) )
30                 except OSError:
31                         print os_path.dirname(cache_file), 'is a file'
32         fd = open(cache_file, 'w')
33         dump(cache_data, fd, -1)
34         fd.close()
35
36 def valid_cache(cache_file, cache_ttl):
37         #See if the cache file exists and is still living
38         try:
39                 mtime = stat(cache_file)[ST_MTIME]
40         except:
41                 return 0
42         curr_time = time()
43         if (curr_time - mtime) > cache_ttl:
44                 return 0
45         else:
46                 return 1
47
48 def load_cache(cache_file):
49         #Does a cPickle load
50         fd = open(cache_file)
51         cache_data = load(fd)
52         fd.close()
53         return cache_data
54
55 class NetworkDescriptor:
56         def __init__(self, name = "NetworkServer", description = ""):
57                 self.name = name
58                 self.description = description
59
60 class NetworkBrowser(Screen):
61         skin = """
62                 <screen name="NetworkBrowser" position="90,80" size="560,450" title="Network Neighbourhood">
63                         <ePixmap pixmap="skin_default/bottombar.png" position="10,360" size="540,120" zPosition="1" transparent="1" alphatest="on" />
64                         <widget source="list" render="Listbox" position="10,10" size="540,350" zPosition="10" scrollbarMode="showOnDemand">
65                                 <convert type="TemplatedMultiContent">
66                                         {"template": [
67                                                         MultiContentEntryPixmapAlphaTest(pos = (0, 0), size = (48, 48), png = 1), # index 1 is the expandable/expanded/verticalline icon
68                                                         MultiContentEntryText(pos = (50, 4), size = (420, 26), font=2, flags = RT_HALIGN_LEFT, text = 2), # index 2 is the Hostname
69                                                         MultiContentEntryText(pos = (140, 5), size = (320, 25), font=0, flags = RT_HALIGN_LEFT, text = 3), # index 3 is the sharename
70                                                         MultiContentEntryText(pos = (140, 26), size = (320, 17), font=1, flags = RT_HALIGN_LEFT, text = 4), # index 4 is the sharedescription
71                                                         MultiContentEntryPixmapAlphaTest(pos = (45, 0), size = (48, 48), png = 5), # index 5 is the nfs/cifs icon
72                                                         MultiContentEntryPixmapAlphaTest(pos = (90, 0), size = (48, 48), png = 6), # index 6 is the isMounted icon
73                                                 ],
74                                         "fonts": [gFont("Regular", 20),gFont("Regular", 14),gFont("Regular", 24)],
75                                         "itemHeight": 50
76                                         }
77                                 </convert>
78                         </widget>
79                         <ePixmap pixmap="skin_default/buttons/button_green.png" position="30,370" zPosition="10" size="15,16" transparent="1" alphatest="on" />
80                         <widget name="mounttext" position="50,370" size="250,21" zPosition="10" font="Regular;21" transparent="1" />
81                         <ePixmap pixmap="skin_default/buttons/button_blue.png" position="30,395" zPosition="10" size="15,16" transparent="1" alphatest="on" />
82                         <widget name="searchtext" position="50,395" size="150,21" zPosition="10" font="Regular;21" transparent="1" />
83                         <widget name="infotext" position="300,375" size="250,21" zPosition="10" font="Regular;21" transparent="1" />
84                         <ePixmap pixmap="skin_default/buttons/button_red.png" position="410,420" zPosition="10" size="15,16" transparent="1" alphatest="on" />
85                         <widget name="closetext" position="430,420" size="120,21" zPosition="10" font="Regular;21" transparent="1" />
86                         <ePixmap pixmap="skin_default/buttons/button_yellow.png" position="30,420" zPosition="10" size="15,16" transparent="1" alphatest="on" />
87                         <widget name="rescantext" position="50,420" size="300,21" zPosition="10" font="Regular;21" transparent="1" />
88                 </screen>"""
89
90         def __init__(self, session, iface,plugin_path):
91                 Screen.__init__(self, session)
92                 self.skin_path = plugin_path
93                 self.session = session
94                 self.iface = iface
95                 if self.iface is None:
96                         self.iface = 'eth0'
97                 self.networklist = None
98                 self.device = None
99                 self.mounts = None
100                 self.expanded = []
101                 self.cache_ttl = 604800 #Seconds cache is considered valid, 7 Days should be ok
102                 self.cache_file = '/etc/enigma2/networkbrowser.cache' #Path to cache directory
103
104                 self["closetext"] = Label(_("Close"))
105                 self["mounttext"] = Label(_("Mounts management"))
106                 self["rescantext"] = Label(_("Rescan network"))
107                 self["infotext"] = Label(_("Press OK to mount!"))
108                 self["searchtext"] = Label(_("Scan IP"))
109
110                 self["shortcuts"] = ActionMap(["ShortcutActions", "WizardActions"],
111                 {
112                         "ok": self.go,
113                         "back": self.close,
114                         "red": self.close,
115                         "green": self.keyGreen,
116                         "yellow": self.keyYellow,
117                         "blue": self.keyBlue,
118                 })
119
120                 self.list = []
121                 self.statuslist = []
122                 self.listindex = 0
123                 self["list"] = List(self.list)
124                 self["list"].onSelectionChanged.append(self.selectionChanged)
125
126                 self.onLayoutFinish.append(self.startRun)
127                 self.onShown.append(self.setWindowTitle)
128                 self.onClose.append(self.cleanup)
129                 self.Timer = eTimer()
130                 self.Timer.callback.append(self.TimerFire)
131
132         def cleanup(self):
133                 del self.Timer
134                 iAutoMount.stopMountConsole()
135                 iNetwork.stopRestartConsole()
136                 iNetwork.stopGetInterfacesConsole()
137
138         def startRun(self):
139                 self.setStatus('update')
140                 self.mounts = iAutoMount.getMountsList()
141                 self["infotext"].hide()
142                 self.vc = valid_cache(self.cache_file, self.cache_ttl)
143                 if self.cache_ttl > 0 and self.vc != 0:
144                         self.process_NetworkIPs()
145                 else:
146                         self.Timer.start(3000)
147
148         def TimerFire(self):
149                 self.Timer.stop()
150                 self.process_NetworkIPs()
151
152         def setWindowTitle(self):
153                 self.setTitle(_("Browse network neighbourhood"))
154
155         def keyGreen(self):
156                 self.session.open(AutoMountManager, None, self.skin_path)
157
158         def keyYellow(self):
159                 if (os_path.exists(self.cache_file) == True):
160                         remove(self.cache_file)
161                 self.startRun()
162
163         def keyBlue(self):
164                 self.session.openWithCallback(self.scanIPclosed,ScanIP)
165
166         def scanIPclosed(self,result):
167                 if result:
168                         print "got IP:",result
169                         nwlist = []
170                         if len(result):
171                                 strIP = str(result) + "/24"
172                                 nwlist.append(netscan.netzInfo(strIP))
173                                 self.networklist = nwlist[0]
174                 if len(self.networklist) > 0:
175                         self.updateHostsList()
176
177         def setStatus(self,status = None):
178                 if status:
179                         self.statuslist = []
180                         if status == 'update':
181                                 statuspng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_PLUGINS, "SystemPlugins/NetworkBrowser/icons/update.png"))
182                                 self.statuslist.append(( ['info'], statuspng, _("Searching your network. Please wait..."), None, None, None, None ))
183                                 self['list'].setList(self.statuslist)
184                         elif status == 'error':
185                                 statuspng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_PLUGINS, "SystemPlugins/NetworkBrowser/icons/error.png"))
186                                 self.statuslist.append(( ['info'], statuspng, _("No network devices found!"), None, None, None, None ))
187                                 self['list'].setList(self.statuslist)
188
189         def process_NetworkIPs(self):
190                 self.inv_cache = 0
191                 self.vc = valid_cache(self.cache_file, self.cache_ttl)
192                 if self.cache_ttl > 0 and self.vc != 0:
193                         print 'Loading network cache from ',self.cache_file
194                         try:
195                                 self.networklist = load_cache(self.cache_file)
196                         except:
197                                 self.inv_cache = 1
198                 if self.cache_ttl == 0 or self.inv_cache == 1 or self.vc == 0:
199                         print 'Getting fresh network list'
200                         self.networklist = self.getNetworkIPs()
201                         write_cache(self.cache_file, self.networklist)
202                 if len(self.networklist) > 0:
203                         self.updateHostsList()
204                 else:
205                         self.setStatus('error')
206
207         def getNetworkIPs(self):
208                 nwlist = []
209                 sharelist = []
210                 self.IP = iNetwork.getAdapterAttribute(self.iface, "ip")
211                 if len(self.IP):
212                         strIP = str(self.IP[0]) + "." + str(self.IP[1]) + "." + str(self.IP[2]) + ".0/24"
213                         nwlist.append(netscan.netzInfo(strIP))
214                 tmplist = nwlist[0]
215                 return tmplist
216
217         def getNetworkShares(self,hostip,hostname,devicetype):
218                 sharelist = []
219                 self.sharecache_file = None
220                 self.sharecache_file = '/etc/enigma2/' + hostname.strip() + '.cache' #Path to cache directory
221                 if os_path.exists(self.sharecache_file):
222                         print 'Loading userinfo from ',self.sharecache_file
223                         try:
224                                 self.hostdata = load_cache(self.sharecache_file)
225                                 username = self.hostdata['username']
226                                 password = self.hostdata['password']
227                         except:
228                                 username = "username"
229                                 password = "password"
230                 else:
231                         username = "username"
232                         password = "password"
233
234                 if devicetype == 'unix':
235                         smblist=netscan.smbShare(hostip,hostname,username,password)
236                         for x in smblist:
237                                 if len(x) == 6:
238                                         if x[3] != 'IPC$':
239                                                 sharelist.append(x)
240                         nfslist=netscan.nfsShare(hostip,hostname)
241                         for x in nfslist:
242                                 if len(x) == 6:
243                                         sharelist.append(x)
244                 else:
245                         smblist=netscan.smbShare(hostip,hostname,username,password)
246                         for x in smblist:
247                                 if len(x) == 6:
248                                         if x[3] != 'IPC$':
249                                                 sharelist.append(x)
250                 return sharelist
251
252         def updateHostsList(self):
253                 self.list = []
254                 self.network = {}
255                 for x in self.networklist:
256                         if not self.network.has_key(x[2]):
257                                 self.network[x[2]] = []
258                         self.network[x[2]].append((NetworkDescriptor(name = x[1], description = x[2]), x))
259                 self.network.keys().sort()
260                 for x in self.network.keys():
261                         hostentry = self.network[x][0][1]
262                         name = hostentry[2] + " ( " +hostentry[1].strip() + " )"
263                         print hostentry
264                         expandableIcon = LoadPixmap(cached=True, path=resolveFilename(SCOPE_PLUGINS, "SystemPlugins/NetworkBrowser/icons/host.png"))
265                         self.list.append(( hostentry, expandableIcon, name, None, None, None, None ))
266                 self["list"].setList(self.list)
267                 self["list"].setIndex(self.listindex)
268
269         def updateNetworkList(self):
270                 self.list = []
271                 self.network = {}
272                 for x in self.networklist:
273                         if not self.network.has_key(x[2]):
274                                 self.network[x[2]] = []
275                         self.network[x[2]].append((NetworkDescriptor(name = x[1], description = x[2]), x))
276                 self.network.keys().sort()
277                 for x in self.network.keys():
278                         if self.network[x][0][1][3] == '00:00:00:00:00:00':
279                                 self.device = 'unix'
280                         else:
281                                 self.device = 'windows'
282                         if x in self.expanded:
283                                 networkshares = self.getNetworkShares(x,self.network[x][0][1][1].strip(),self.device)
284                                 hostentry = self.network[x][0][1]
285                                 name = hostentry[2] + " ( " +hostentry[1].strip() + " )"
286                                 expandedIcon = LoadPixmap(cached=True, path=resolveFilename(SCOPE_PLUGINS, "SystemPlugins/NetworkBrowser/icons/host.png"))
287                                 self.list.append(( hostentry, expandedIcon, name, None, None, None, None ))
288                                 for share in networkshares:
289                                         self.list.append(self.BuildNetworkShareEntry(share))
290                         else: # HOSTLIST - VIEW
291                                 hostentry = self.network[x][0][1]
292                                 name = hostentry[2] + " ( " +hostentry[1].strip() + " )"
293                                 expandableIcon = LoadPixmap(cached=True, path=resolveFilename(SCOPE_PLUGINS, "SystemPlugins/NetworkBrowser/icons/host.png"))
294                                 self.list.append(( hostentry, expandableIcon, name, None, None, None, None ))
295                 self["list"].setList(self.list)
296                 self["list"].setIndex(self.listindex)
297
298         def BuildNetworkShareEntry(self,share):
299                 verticallineIcon = LoadPixmap(cached=True, path=resolveFilename(SCOPE_PLUGINS, "SystemPlugins/NetworkBrowser/icons/verticalLine.png"))
300                 sharetype = share[0]
301                 localsharename = share[1]
302                 sharehost = share[2]
303
304                 if sharetype == 'smbShare':
305                         sharedir = share[3]
306                         sharedescription = share[5]
307                 else:
308                         sharedir = share[4]
309                         sharedescription = share[3]
310
311                 if sharetype == 'nfsShare':
312                         newpng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_PLUGINS, "SystemPlugins/NetworkBrowser/icons/i-nfs.png"))
313                 else:
314                         newpng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_PLUGINS, "SystemPlugins/NetworkBrowser/icons/i-smb.png"))
315
316                 self.isMounted = False
317                 for sharename, sharedata in self.mounts.items():
318                         if sharedata['ip'] == sharehost:
319                                 if sharetype == 'nfsShare' and sharedata['mounttype'] == 'nfs':
320                                         if sharedir == sharedata['sharedir']:
321                                                 if sharedata["isMounted"] is True:
322                                                         self.isMounted = True
323                                 if sharetype == 'smbShare' and sharedata['mounttype'] == 'cifs':
324                                         if sharedir == sharedata['sharedir']:
325                                                 if sharedata["isMounted"] is True:
326                                                         self.isMounted = True
327                 if self.isMounted is True:
328                         isMountedpng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_PLUGINS, "SystemPlugins/NetworkBrowser/icons/ok.png"))
329                 else:
330                         isMountedpng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_PLUGINS, "SystemPlugins/NetworkBrowser/icons/cancel.png"))
331
332                 return((share, verticallineIcon, None, sharedir, sharedescription, newpng, isMountedpng))
333
334         def selectionChanged(self):
335                 current = self["list"].getCurrent()
336                 self.listindex = self["list"].getIndex()
337                 print len(current)
338                 if current:
339                         if current[0][0] in ("nfsShare", "smbShare"):
340                                 self["infotext"].show()
341                         else:
342                                 self["infotext"].hide()
343
344         def go(self):
345                 sel = self["list"].getCurrent()
346                 if sel is None:
347                         return
348                 if len(sel[0]) <= 1:
349                         return
350                 selectedhost = sel[0][2]
351                 selectedhostname = sel[0][1]
352
353                 self.hostcache_file = None
354                 if sel[0][0] == 'host': # host entry selected
355                         if selectedhost in self.expanded:
356                                 self.expanded.remove(selectedhost)
357                                 self.updateNetworkList()
358                         else:
359                                 self.hostcache_file = '/etc/enigma2/' + selectedhostname.strip() + '.cache' #Path to cache directory
360                                 if os_path.exists(self.hostcache_file):
361                                         print 'Loading userinfo cache from ',self.hostcache_file
362                                         try:
363                                                 self.hostdata = load_cache(self.hostcache_file)
364                                                 self.passwordQuestion(False)
365                                         except:
366                                                 self.session.openWithCallback(self.passwordQuestion, MessageBox, (_("Do you want to enter a username and password for this host?\n") ) )
367                                 else:
368                                         self.session.openWithCallback(self.passwordQuestion, MessageBox, (_("Do you want to enter a username and password for this host?\n") ) )
369                 if sel[0][0] == 'nfsShare': # share entry selected
370                         self.openMountEdit(sel[0])
371                 if sel[0][0] == 'smbShare': # share entry selected
372                         self.openMountEdit(sel[0])
373
374         def passwordQuestion(self, ret = False):
375                 sel = self["list"].getCurrent()
376                 selectedhost = sel[0][2]
377                 selectedhostname = sel[0][1]
378                 if (ret == True):
379                         self.session.openWithCallback(self.UserDialogClosed, UserDialog, self.skin_path, selectedhostname.strip())
380                 else:
381                         if sel[0][0] == 'host': # host entry selected
382                                 if selectedhost in self.expanded:
383                                         self.expanded.remove(selectedhost)
384                                 else:
385                                         self.expanded.append(selectedhost)
386                                 self.updateNetworkList()
387                         if sel[0][0] == 'nfsShare': # share entry selected
388                                 self.openMountEdit(sel[0])
389                         if sel[0][0] == 'smbShare': # share entry selected
390                                 self.openMountEdit(sel[0])
391
392         def UserDialogClosed(self, *ret):
393                 if ret is not None and len(ret):
394                         self.go()
395
396         def openMountEdit(self, selection):
397                 if selection is not None and len(selection):
398                         mounts = iAutoMount.getMountsList()
399                         if selection[0] == 'nfsShare': # share entry selected
400                                 #Initialize blank mount enty
401                                 data = { 'isMounted': False, 'active': False, 'ip': False, 'sharename': False, 'sharedir': False, 'username': False, 'password': False, 'mounttype' : False, 'options' : False }
402                                 # add data
403                                 data['mounttype'] = 'nfs'
404                                 data['active'] = True
405                                 data['ip'] = selection[2]
406                                 data['sharename'] = selection[1]
407                                 data['sharedir'] = selection[4]
408                                 data['options'] = "rw,nolock"
409
410                                 for sharename, sharedata in mounts.items():
411                                         if sharedata['ip'] == selection[2] and sharedata['sharedir'] == selection[4]:
412                                                 data = sharedata
413                                 self.session.openWithCallback(self.MountEditClosed,AutoMountEdit, self.skin_path, data)
414                         if selection[0] == 'smbShare': # share entry selected
415                                 #Initialize blank mount enty
416                                 data = { 'isMounted': False, 'active': False, 'ip': False, 'sharename': False, 'sharedir': False, 'username': False, 'password': False, 'mounttype' : False, 'options' : False }
417                                 # add data
418                                 data['mounttype'] = 'cifs'
419                                 data['active'] = True
420                                 data['ip'] = selection[2]
421                                 data['sharename'] = selection[1]
422                                 data['sharedir'] = selection[3]
423                                 data['options'] = "rw"
424                                 self.sharecache_file = None
425                                 self.sharecache_file = '/etc/enigma2/' + selection[1].strip() + '.cache' #Path to cache directory
426                                 if os_path.exists(self.sharecache_file):
427                                         print 'Loading userinfo from ',self.sharecache_file
428                                         try:
429                                                 self.hostdata = load_cache(self.sharecache_file)
430                                                 print "self.hostdata", self.hostdata
431                                                 data['username'] = self.hostdata['username']
432                                                 data['password'] = self.hostdata['password']
433                                         except:
434                                                 data['username'] = "username"
435                                                 data['password'] = "password"
436                                 else:
437                                         data['username'] = "username"
438                                         data['password'] = "password"
439
440                                 for sharename, sharedata in mounts.items():
441                                         if sharedata['ip'] == selection[2].strip() and sharedata['sharedir'] == selection[3].strip():
442                                                 data = sharedata
443                                 self.session.openWithCallback(self.MountEditClosed,AutoMountEdit, self.skin_path, data)
444
445         def MountEditClosed(self, returnValue = None):
446                 if returnValue == None:
447                         self.updateNetworkList()
448
449 class ScanIP(Screen):
450         skin = """
451                 <screen name="IPKGSource" position="100,100" size="550,80" title="IPKG source" >
452                         <widget name="text" position="10,10" size="530,25" font="Regular;20" backgroundColor="background" foregroundColor="#cccccc" />
453                         <ePixmap pixmap="skin_default/buttons/red.png" position="10,40" zPosition="2" size="140,40" transparent="1" alphatest="on" />
454                         <widget name="closetext" position="20,50" size="140,21" zPosition="10" font="Regular;21" transparent="1" />
455                         <ePixmap pixmap="skin_default/buttons/green.png" position="160,40" zPosition="2" size="140,40" transparent="1" alphatest="on" />
456                         <widget name="edittext" position="170,50" size="300,21" zPosition="10" font="Regular;21" transparent="1" />
457                 </screen>"""
458
459         def __init__(self, session):
460                 Screen.__init__(self, session)
461                 self.session = session
462                 text = ""
463
464                 desk = getDesktop(0)
465                 x= int(desk.size().width())
466                 y= int(desk.size().height())
467                 #print "[IPKGSource] mainscreen: current desktop size: %dx%d" % (x,y)
468
469                 self["closetext"] = Label(_("Cancel"))
470                 self["edittext"] = Label(_("OK"))
471
472                 if (y>=720):
473                         self["text"] = Input(text, maxSize=False, type=Input.TEXT)
474                 else:
475                         self["text"] = Input(text, maxSize=False, visible_width = 55, type=Input.TEXT)
476
477                 self["actions"] = NumberActionMap(["WizardActions", "InputActions", "TextEntryActions", "KeyboardInputActions","ShortcutActions"],
478                 {
479                         "ok": self.go,
480                         "back": self.exit,
481                         "red": self.exit,
482                         "green": self.go,
483                         "left": self.keyLeft,
484                         "right": self.keyRight,
485                         "home": self.keyHome,
486                         "end": self.keyEnd,
487                         "deleteForward": self.keyDeleteForward,
488                         "deleteBackward": self.keyDeleteBackward,
489                         "1": self.keyNumberGlobal,
490                         "2": self.keyNumberGlobal,
491                         "3": self.keyNumberGlobal,
492                         "4": self.keyNumberGlobal,
493                         "5": self.keyNumberGlobal,
494                         "6": self.keyNumberGlobal,
495                         "7": self.keyNumberGlobal,
496                         "8": self.keyNumberGlobal,
497                         "9": self.keyNumberGlobal,
498                         "0": self.keyNumberGlobal
499                 }, -1)
500
501                 self.onLayoutFinish.append(self.layoutFinished)
502
503         def exit(self):
504                 self.close(None)
505
506         def layoutFinished(self):
507                 self.setWindowTitle()
508                 self["text"].right()
509
510         def setWindowTitle(self):
511                 self.setTitle(_("Enter IP to scan..."))
512
513         def go(self):
514                 text = self["text"].getText()
515                 if text:
516                         self.close(text)
517
518         def keyLeft(self):
519                 self["text"].left()
520
521         def keyRight(self):
522                 self["text"].right()
523
524         def keyHome(self):
525                 self["text"].home()
526
527         def keyEnd(self):
528                 self["text"].end()
529
530         def keyDeleteForward(self):
531                 self["text"].delete()
532
533         def keyDeleteBackward(self):
534                 self["text"].deleteBackward()
535
536         def keyNumberGlobal(self, number):
537                 print "pressed", number
538                 self["text"].number(number)
539