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